package Lono::Util::Logger;

use strict;
use warnings;

=head1 NAME

Lono::Util::Logger - Simple logger.

=head1 SYNOPSIS

  use Lono::Util::Logger;
  # Exports log level constants.

  my $logger = Lono::Util::Logger->new;
  $logger->warning( "Something bad has happened." );
  $logger->error( "Something worse has happened." );
  $logger->critical( "The unspeakable has happened." );

  $logger->append( {'severity' => LOG_DEBUG,
                    'message'  => 'Just letting you know...'} );

  my @all_entries = $logger->get_entries;
  my @error_and_critical_entries = $logger->get_entries( LOG_ERROR );

=head1 DESCRIPTION

This class exports 5 constants: LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, and LOG_CRITICAL.

=cut

use Exporter;
our @ISA = qw( Exporter );

use constant LOG_DEBUG    => 'debug';
use constant LOG_INFO     => 'info';
use constant LOG_WARNING  => 'warning';
use constant LOG_ERROR    => 'error';
use constant LOG_CRITICAL => 'critical';

my @ORDER = ( LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_CRITICAL );
our @EXPORT = qw( LOG_DEBUG LOG_INFO LOG_WARNING LOG_ERROR LOG_CRITICAL );

=head1 FUNCTIONS

=over 4

=item new()

Simple constructor.

=cut

sub new {
    my $class = shift;
    my $self = { '_messages' => [] };
    bless $self, $class;
    return $self;
}

=item get_messages( 'level' )

Returns a list of messages.  Takes an argument of log level.  If passed in, this function will filter the list of messages returned to the specified log level or higher.

=cut

sub get_messages {
    my $self = shift;
    my $level = LOG_DEBUG;
    if ( @_ ) {
        $level = shift;
    }

    return ( $level eq LOG_DEBUG ) ?
        @{$self->{'_messages'}} : 
            $self->_filter_to_level( $level );
}

sub _filter_to_level {
    my ( $self, $level ) = @_;
    my @FILTER = @ORDER;
    my $this_level = $FILTER[0];
    
    while ( $level ne $this_level ) {
        shift @FILTER;
        $this_level = $FILTER[0];     
    }

    my @FILTERED = grep length, 
                   map { ( _high_enough( $_->{'severity'}, @FILTER ) )
                         ? $_ : '' } 
                   @{$self->{'_messages'}};
    return @FILTERED;
}

sub _high_enough {
    my ( $severity, @FILTER ) = @_;
    foreach my $item ( @FILTER ) {
        if ( $severity eq $item ) {
            return 1;
        }
    }
    return 0;
}

=item append( {'severity' => LOG_SEVERITY, 'message' => 'log message.'} )

Adds a message (with the given severity) to the list.

=cut

sub append {
    my ( $self, $args ) = @_;
    push( @{$self->{'_messages'}}, $args );
}

=item debug( $message )

Append a debug level message.

=cut

sub debug {
    my ( $self, $message ) = @_;
    $self->append( {'severity' => LOG_DEBUG, 'message' => $message} );
}

=item info( $message )

Append an info level message.

=cut

sub info {
    my ( $self, $message ) = @_;
    $self->append( {'severity' => LOG_INFO, 'message' => $message} );
}

=item warning( $message )

Append a warning level message.

=cut

sub warning {
    my ( $self, $message ) = @_;
    $self->append( {'severity' => LOG_WARNING, 'message' => $message} );
}

=item error( $message )

Append an error level message.

=cut

sub error {
    my ( $self, $message ) = @_;
    $self->append( {'severity' => LOG_ERROR, 'message' => $message} );
}

=item critical( $message )

Append a critical level message.

=cut

sub critical {
    my ( $self, $message ) = @_;
    $self->append( {'severity' => LOG_CRITICAL, 'message' => $message} );
}

1;
